/** @file
Iort table platform libary.

Copyright (C) 2023, Phytium Technology Co Ltd. All rights reserved.<BR>

SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <PiDxe.h>
#include <Library/ArmLib.h>
#include <Library/DebugLib.h>
#include <Library/IoLib.h>
#include <Library/PssiLib.h>
#include <Library/HobLib.h>
#include <Library/PcdLib.h>
#include <Library/ParameterTable.h>
#include <Protocol/AcpiTable.h>
#include <Protocol/ConfigurationManagerProtocol.h>
#include <IndustryStandard/IoRemappingTable.h>
#include <PhytiumAcpiHelp.h>
#include <AcpiPlatform.h>
#include "IortTableLib.h"

#define AML_PATH_SIZE 10

PLATFORM_INFO            gPlatformInfo;
UINT8                    gSocketCount;
UINT8                    gSmmuV3Count;
UINT8                    gRootComplexCount;
UINT8                    gItsGroupCount;
UINT8                    gNamedComponentCount;

CHAR8 CeuNode0[AML_PATH_SIZE + 1];
CHAR8 CeuNode1[AML_PATH_SIZE + 1];
CHAR8 CeuNode2[AML_PATH_SIZE + 1];
CHAR8 CeuNode3[AML_PATH_SIZE + 1];
CHAR8 CeuNode4[AML_PATH_SIZE + 1];
CHAR8 CeuNode5[AML_PATH_SIZE + 1];
CHAR8 CeuNode6[AML_PATH_SIZE + 1];
CHAR8 CeuNode7[AML_PATH_SIZE + 1];

CHAR8 *ObjectName[] = {
  CeuNode0,
  CeuNode1,
  CeuNode2,
  CeuNode3,
  CeuNode4,
  CeuNode5,
  CeuNode6,
  CeuNode7
};

/**
  Fill the IdMapping Struct.

  @param [in]  Index         Index to be fill.
  @param [in]  Start         Start of IdMapping.
  @param [in]  End           End of IdMapping.
  @param [in]  Token         OutputReferenceToken of IdMapping.
  @param [in]  Flags         Flags of IdMapping.
**/
VOID
FillIdMappingInfo (
  IN UINT8  Index,
  IN UINT8  Start,
  IN UINT8  End,
  IN UINTN  Token,
  IN UINT32 Flags
)
{
  CM_ARM_ID_MAPPING               *Info;

  Info = gPlatformInfo.IdMappingInfo;
  Info[Index].InputBase = (Start << 8);
  Info[Index].NumIds = ((End - Start)) << 8 | 0xFF;
  Info[Index].OutputBase = (Start << 8);
  Info[Index].OutputReferenceToken = Token;
  Info[Index].Flags = Flags;
}

/**
  Print All IdMappingInfo.
**/
VOID
DumpIdMappingInfo (
  VOID
  )
{
  CM_ARM_ID_MAPPING                 *Info;
  UINT8                             Index;
  UINT8                             SocketIndex;
  UINT8                             RootComplexIdMappingIndex;
  UINT8                             Smmu0IdMappingIndex;
  UINT8                             Smmu1IdMappingIndex;
  UINT8                             Smmu2IdMappingIndex;

  Info = gPlatformInfo.IdMappingInfo;

  for (SocketIndex = 0; SocketIndex < gSocketCount; SocketIndex++) {
    DEBUG ((DEBUG_INFO, "SocketId:%d\n", SocketIndex));
    RootComplexIdMappingIndex = SocketIndex * 8 + ROOT_COMPLEX_ID_MAPPING_OFFSET;
    DEBUG ((DEBUG_INFO, "RootComplex IdMapping Info:\n"));
    for (Index = 0; Index < gPlatformInfo.RootComplexIdMappingCount[SocketIndex]; Index++) {
      DEBUG ((
        DEBUG_INFO,
        "IdMapping[%d] InputBase = 0x%x, NumIds = 0x%x, OutputBase = 0x%x,\
        OutputReferenceToken = %d, Flags = %d\n",
        Index,
        Info[RootComplexIdMappingIndex + Index].InputBase,
        Info[RootComplexIdMappingIndex + Index].NumIds,
        Info[RootComplexIdMappingIndex + Index].InputBase,
        Info[RootComplexIdMappingIndex + Index].OutputReferenceToken,
        Info[RootComplexIdMappingIndex + Index].Flags));
    }

    DEBUG ((DEBUG_INFO, "Smmu0 IdMapping Info:\n"));
    Smmu0IdMappingIndex = SocketIndex * 8 + SMMUV3_NODE_0_ID_MAPPING_OFFSET;
    for (Index = 0; Index < gPlatformInfo.Smmu0IdMappingCount[SocketIndex]; Index++) {
      DEBUG ((
        DEBUG_INFO,
        "IdMapping[%d] InputBase = 0x%x, NumIds = 0x%x, OutputBase = 0x%x,\
        OutputReferenceToken = %d, Flags = %d\n",
        Index,
        Info[Smmu0IdMappingIndex + Index].InputBase,
        Info[Smmu0IdMappingIndex + Index].NumIds,
        Info[Smmu0IdMappingIndex + Index].InputBase,
        Info[Smmu0IdMappingIndex + Index].OutputReferenceToken,
        Info[Smmu0IdMappingIndex + Index].Flags
        ));
    }

    DEBUG ((DEBUG_INFO, "Smmu1 IdMapping Info:\n"));
    Smmu1IdMappingIndex = SocketIndex * 8 + SMMUV3_NODE_1_ID_MAPPING_OFFSET;
    DEBUG ((
      DEBUG_INFO,
      "IdMapping[%d] InputBase = 0x%x, NumIds = 0x%x, OutputBase = 0x%x,\
      OutputReferenceToken = %d, Flags = %d\n",
      Index,
      Info[Smmu1IdMappingIndex].InputBase,
      Info[Smmu1IdMappingIndex].NumIds,
      Info[Smmu1IdMappingIndex].InputBase,
      Info[Smmu1IdMappingIndex].OutputReferenceToken,
      Info[Smmu1IdMappingIndex].Flags
      ));

    DEBUG ((DEBUG_INFO, "Smmu2 IdMapping Info:\n"));
    Smmu2IdMappingIndex = SocketIndex * 8 + SMMUV3_NODE_2_ID_MAPPING_OFFSET;
    DEBUG (( DEBUG_INFO, "IdMapping[%d] InputBase = 0x%x, NumIds = 0x%x, OutputBase = 0x%x,\
      OutputReferenceToken = %d, Flags = %d\n",
      Index,
      Info[Smmu2IdMappingIndex].InputBase,
      Info[Smmu2IdMappingIndex].NumIds,
      Info[Smmu2IdMappingIndex].InputBase,
      Info[Smmu2IdMappingIndex].OutputReferenceToken,
      Info[Smmu2IdMappingIndex].Flags));
  }
}

/**
  Get Pci bifurcation.

  @param [in]  SocketId  Socket Id.
**/
UINT32
GetPciBifurcation (
  IN UINT8 SocketId
  )
{
  EFI_STATUS                Status;
  UINT8                     Buffer[0x100];
  PARAMETER_PCIE_CONFIG_V3  *PcieConfig;

  Status = GetParameterPcieV3 (PM_PCIE_V3, SocketId, Buffer, 0x100);
  if (EFI_ERROR(Status)) {
    DEBUG((DEBUG_ERROR, "Get Pcie Parameter Fail.\n"));
    return -1;
  } else {
    PcieConfig = (PARAMETER_PCIE_CONFIG_V3 *)Buffer;
    return PcieConfig->Split.Uint32;
  }
}

/**
  Init Gic Its Identifier info.
**/
VOID
InitGicItsIdentifierArrayInfo (
  VOID
)
{
  CM_ARM_ITS_IDENTIFIER           *Info;
  UINT8                           Index;
  UINT8                           SocketIndex;
  UINT8                           IdentifierIndex;

  Info = gPlatformInfo.ItsIdentifierInfo;

  for (SocketIndex = 0; SocketIndex < gSocketCount; SocketIndex++) {
    for (Index = 0; Index < GIC_ITS_INDENTIFEIR_COUNT; Index++) {
      IdentifierIndex = SocketIndex * 4 + Index;
      Info[IdentifierIndex].ItsId = SocketIndex * 4 + ITS_ID_TOKEN + Index;
    }
  }
}

/**
  Init ItsGroup Info.
**/
VOID
InitItsGroupInfo (
  VOID
  )
{
  CM_ARM_ITS_GROUP_NODE           *Info;
  UINT8                           ItsGroupNodeIndex;
  UINT8                           SocketIndex;
  UINT8                           Pci0Sub0Bifurcation;
  UINT8                           Pci0Sub1Bifurcation;
  UINT8                           Pci1Bifurcation;
  UINT8                           Pci2Bifurcation;
  UINT32                          PciBifurcation;


  Info = gPlatformInfo.ItsGroupInfo;
  ItsGroupNodeIndex = 0;
  for (SocketIndex = 0; SocketIndex < gSocketCount; SocketIndex++) {
    PciBifurcation = GetPciBifurcation (SocketIndex);
    Pci0Sub0Bifurcation = PciBifurcation & 0xF;
    Pci0Sub1Bifurcation = (PciBifurcation >> 4) & 0xF;
    Pci1Bifurcation = (PciBifurcation >> 8) & 0xF;
    Pci2Bifurcation = (PciBifurcation >> 12) & 0xF;
    //Its0
    if ((PCI_BYPASS != Pci1Bifurcation) || ((C2C_C2C != Pci1Bifurcation))) {
      Info[ItsGroupNodeIndex].Token = (SocketIndex << 8) | ITS_NODE_0_TOKEN;
      Info[ItsGroupNodeIndex].ItsIdCount = 0x1;
      Info[ItsGroupNodeIndex].ItsIdToken = SocketIndex * 4 + ITS_0_ID_TOKEN;
      ItsGroupNodeIndex++;
    }

    //Its1
    if ((PCI_BYPASS != Pci2Bifurcation) || ((C2C_C2C != Pci2Bifurcation))) {
      Info[ItsGroupNodeIndex].Token = (SocketIndex << 8) | ITS_NODE_1_TOKEN;
      Info[ItsGroupNodeIndex].ItsIdCount = 0x1;
      Info[ItsGroupNodeIndex].ItsIdToken = SocketIndex * 4 + ITS_1_ID_TOKEN;
      ItsGroupNodeIndex++;
    }

    //Its2
    if (PCI_BYPASS != Pci0Sub0Bifurcation) {
      Info[ItsGroupNodeIndex].Token = (SocketIndex << 8) | ITS_NODE_2_TOKEN;
      Info[ItsGroupNodeIndex].ItsIdCount = 0x1;
      Info[ItsGroupNodeIndex].ItsIdToken = SocketIndex * 4 + ITS_2_ID_TOKEN;
      ItsGroupNodeIndex++;
    }

    //Its3
    if ((Pci0Sub1Bifurcation > PCI_BYPASS) || ((C2C_C2C != Pci0Sub1Bifurcation))) {
      Info[ItsGroupNodeIndex].Token = (SocketIndex << 8) | ITS_NODE_3_TOKEN;
      Info[ItsGroupNodeIndex].ItsIdCount = 0x1;
      Info[ItsGroupNodeIndex].ItsIdToken = SocketIndex * 4 + ITS_3_ID_TOKEN;
      ItsGroupNodeIndex++;
    }
  }

  gItsGroupCount = ItsGroupNodeIndex;
}

VOID
DumpItsGroupInfo (
  VOID
  )
{
  CM_ARM_ITS_GROUP_NODE  *Info;
  UINT8                  Index;

  Info = gPlatformInfo.ItsGroupInfo;
  for (Index = 0; Index < gItsGroupCount; Index++) {
    DEBUG ((DEBUG_INFO, "ItsGroup[%d].Token:0x%x\n", Index, Info[Index].Token));
    DEBUG ((DEBUG_INFO, "ItsGroup[%d].ItsIdToken:0x%x\n", Index, Info[Index].ItsIdToken));
    DEBUG ((DEBUG_INFO, "\n"));
  }
}

/**
  Init RootComplex Info.
**/
VOID
InitRootComplexInfo (
  VOID
  )
{
  CM_ARM_ROOT_COMPLEX_NODE        *Info;
  UINT8                           RootComplexIndex;
  UINT8                           SocketIndex;
  UINT8                           Index;
  UINT8                           Pci0Sub0Bifurcation;
  UINT8                           Pci0Sub1Bifurcation;
  UINT8                           Pci1Bifurcation;
  UINT8                           Pci2Bifurcation;
  UINT32                          PciBifurcation;


  Info = gPlatformInfo.RootComplexInfo;

  for (SocketIndex = 0; SocketIndex < gSocketCount; SocketIndex++) {
    for (Index = 0; Index < ROOT_COMPLEX_COUNT; Index++) {
      PciBifurcation = GetPciBifurcation (SocketIndex);
      Pci0Sub0Bifurcation = PciBifurcation & 0xF;
      Pci0Sub1Bifurcation = (PciBifurcation >> 4) & 0xF;
      Pci1Bifurcation = (PciBifurcation >> 8) & 0xF;
      Pci2Bifurcation = (PciBifurcation >> 12) & 0xF;
      if ((PCI_BYPASS == Pci0Sub0Bifurcation) &&  \
           (PCI_BYPASS == Pci0Sub1Bifurcation) && \
           ((PCI_BYPASS == Pci1Bifurcation) || (C2C_C2C == Pci1Bifurcation)) && \
           ((PCI_BYPASS == Pci2Bifurcation) || (C2C_C2C == Pci2Bifurcation)) \
         )
        continue;
      RootComplexIndex = SocketIndex * ROOT_COMPLEX_COUNT + Index;
      Info[RootComplexIndex].Token = (SocketIndex << 8) | ROOT_COMPLEX_NODE_TOKEN;
      Info[RootComplexIndex].IdMappingCount = gPlatformInfo.RootComplexIdMappingCount[SocketIndex];
      Info[RootComplexIndex].IdMappingToken = (SocketIndex << 8) | ROOT_COMPLEX_ID_MAPPING_TOKEN;
      Info[RootComplexIndex].CacheCoherent = EFI_ACPI_IORT_MEM_ACCESS_PROP_CCA;
      Info[RootComplexIndex].AllocationHints = 0;
      Info[RootComplexIndex].MemoryAccessFlags = EFI_ACPI_IORT_MEM_ACCESS_FLAGS_CPM | EFI_ACPI_IORT_MEM_ACCESS_FLAGS_DACS;
      Info[RootComplexIndex].AtsAttribute = EFI_ACPI_IORT_ROOT_COMPLEX_ATS_UNSUPPORTED;
      Info[RootComplexIndex].PciSegmentNumber = SocketIndex;
      Info[RootComplexIndex].MemoryAddressSize = 48;
      gRootComplexCount++;
    }
  }
}

/**
  Dump Root Complex Info.
**/
VOID
DumpRootComplexInfo (
  VOID
  )
{
  CM_ARM_ROOT_COMPLEX_NODE  *Info;
  UINT8                     Index;

  Info = gPlatformInfo.RootComplexInfo;
  for (Index = 0; Index < gRootComplexCount; Index++) {
    DEBUG ((DEBUG_INFO, "RootComplex[%d].Token:0x%x\n", Index, Info[Index].Token));
    DEBUG ((DEBUG_INFO, "RootComplex[%d].ItsIdToken:0x%x\n", Index, Info[Index].IdMappingCount));
    DEBUG ((DEBUG_INFO, "RootComplex[%d].IdMappingToken:0x%x\n", Index, Info[Index].IdMappingToken));
    DEBUG ((DEBUG_INFO, "\n"));
  }
}

/** Convert a hex number to its ASCII code.

 @param [in]  Hex   Hex number to convert.
                    Must be 0 <= x < 16.

 @return The ASCII code corresponding to x.
         -1 if error.
**/
UINT8
EFIAPI
AsciiFromHex (
  IN  UINT8  Hex
  )
{
  if (Hex < 10) {
    return (UINT8)(Hex + '0');
  }

  if (Hex < 16) {
    return (UINT8)(Hex - 10 + 'A');
  }

  ASSERT (FALSE);
  return (UINT8)-1;
}

/**
  Init Named Component Info.

  @param [in]  EnableCeuSmmu  Control Ceu Smmu Enable/Disable.
**/
VOID
InitNamedComponentInfo (
  IN  BOOLEAN  EnableCeuSmmu
  )
{
  CM_ARM_NAMED_COMPONENT_NODE  *CeuNodeList;
  UINT8                        Index;
  CM_ARM_ID_MAPPING            *IdMappings;
  CHAR8                        AslPath[AML_PATH_SIZE + 1];

  if (!EnableCeuSmmu) {
    return;
  }

  CeuNodeList = gPlatformInfo.CeuNode;
  IdMappings = gPlatformInfo.CeuIdMappings;
  CopyMem (AslPath, "\\_SB_.CEUx", AML_PATH_SIZE + 1);

  for (Index = 0; Index < gSocketCount; Index++) {
    AslPath[AML_PATH_SIZE - 1] = AsciiFromHex (Index);
    CopyMem (ObjectName[Index], AslPath, AML_PATH_SIZE + 1);
    CeuNodeList[Index].Token = (Index << 8) | CEU_NODE_TOKEN;
    CeuNodeList[Index].IdMappingCount = 0x1;
    CeuNodeList[Index].IdMappingToken = (Index << 8) | CEU_ID_MAPPING_TOKEN;
    CeuNodeList[Index].Flags = 0x0;
    CeuNodeList[Index].CacheCoherent = EFI_ACPI_IORT_MEM_ACCESS_PROP_CCA;
    CeuNodeList[Index].AllocationHints = 0x0;
    CeuNodeList[Index].MemoryAccessFlags = EFI_ACPI_IORT_MEM_ACCESS_FLAGS_CPM | EFI_ACPI_IORT_MEM_ACCESS_FLAGS_DACS;
    CeuNodeList[Index].AddressSizeLimit = 48;
    CeuNodeList[Index].ObjectName = ObjectName[Index];

    IdMappings[Index].InputBase = 0x0;
    IdMappings[Index].NumIds = 0xFFFF;
    IdMappings[Index].OutputBase = 0x0;
    IdMappings[Index].OutputReferenceToken = (Index << 8) | (CEU_SMMUV3_NODE_TOKEN);
    IdMappings[Index].Flags = EFI_ACPI_IORT_ID_MAPPING_FLAGS_SINGLE;
  }

  gNamedComponentCount = gSocketCount;
}

/**
  Dump Named Component Info.
**/
VOID
DumpNamedComponentInfo (
  VOID
  )
{
  CM_ARM_NAMED_COMPONENT_NODE  *CeuNodeList;
  CM_ARM_ID_MAPPING            *IdMappings;
  UINT8                        Index;

  CeuNodeList = gPlatformInfo.CeuNode;
  IdMappings = gPlatformInfo.CeuIdMappings;

  DEBUG ((DEBUG_INFO, "Dump NamedComponent:\n"));
  for (Index = 0; Index < gNamedComponentCount; Index++) {
    DEBUG ((DEBUG_INFO, "NamedComponent[%d].Token:0x%x\n", Index, CeuNodeList[Index].Token));
    DEBUG ((DEBUG_INFO, "NamedComponent[%d].IdMappingToken:0x%x\n", Index, CeuNodeList[Index].IdMappingToken));
    DEBUG ((DEBUG_INFO, "NamedComponent[%d].ObjectName:%a\n", Index, CeuNodeList[Index].ObjectName));
    DEBUG ((DEBUG_INFO, "\n"));
  }

  DEBUG ((DEBUG_INFO, "Dump NamedComponent IdMappings:\n"));
  for (Index = 0; Index < gNamedComponentCount; Index++) {
    DEBUG ((
      DEBUG_INFO,
      "IdMapping[%d] InputBase = 0x%x, NumIds = 0x%x, OutputBase = 0x%x,\
      OutputReferenceToken = %d, Flags = %d\n",
      Index,
      IdMappings[Index].InputBase,
      IdMappings[Index].NumIds,
      IdMappings[Index].InputBase,
      IdMappings[Index].OutputReferenceToken,
      IdMappings[Index].Flags));
  }
}

/**
  Get Pci device SubordinateBusNumber.

  @param [in]  SocketId       Socket Id.
  @param [in]  DeviceNumber   pci device number.

  @retval      Subordinate Bus Number.
**/
UINT8
GetSubordinateBusNumber (
  IN UINT8 SocketId,
  IN UINT8 DeviceNumber
  )
{
  UINT64 SubordinateReg;
  UINT8  SubordinateBusNumber;

  SubordinateReg = SocketId * SOCKET_ADDRESS_OFFSEST + PCI_CONFIG_BASE + \
                     DeviceNumber * 0x8000 + PRIMARY_BUS_REG;
  SubordinateBusNumber = (MmioRead32(SubordinateReg) >> 16) & 0xff;

  return SubordinateBusNumber;
}

/**
  Get Pci device SecondaryBusNumber.

  @param [in]  SocketId       Socket Id.
  @param [in]  DeviceNumber   pci device number.

  @retval      Secondary Bus Number.
**/
UINT8
GetSecondaryBusNumber(
  IN UINT8 SocketId,
  IN UINT8 DeviceNumber
  )
{
  UINT64 SecondaryReg;
  UINT8  SecondaryBusNumber;

  SecondaryReg = SocketId * SOCKET_ADDRESS_OFFSEST + PCI_CONFIG_BASE + \
                   DeviceNumber * 0x8000 + PRIMARY_BUS_REG;
  SecondaryBusNumber = (MmioRead32(SecondaryReg) >> 8) & 0xff;

  return SecondaryBusNumber;
}

/**
  Init Socket Count.

  @retval EFI_SUCCESS             Success.
  @retval EFI_NOT_FOUND           The required information is not found.
**/
EFI_STATUS
InitSocketNumber (
  VOID
  )
{
  EFI_HOB_GUID_TYPE                *GuidHob;
  PHYTIUM_CPU_MAP_INFO             *CpuMapInfo;

  GuidHob = GetFirstGuidHob(&gPlatformCpuInforGuid);
  if (GuidHob == NULL) {
     return EFI_NOT_FOUND;
  }
  CpuMapInfo = (PHYTIUM_CPU_MAP_INFO *) GET_GUID_HOB_DATA(GuidHob);
  gSocketCount = CpuMapInfo->CpuMapCount;

  return EFI_SUCCESS;
}

/**
  Init SmmuV3 Info.

  @param [in]  EnablePcieSmmu   Control Pcie Smmu Enable/Disable.
  @param [in]  EnableCeuSmmu    Control Ceu Smmu Enable/Disable.
**/
VOID
InitSmmuV3Info (
  IN  BOOLEAN   EnablePcieSmmu,
  IN  BOOLEAN   EnableCeuSmmu
  )
{
  CM_ARM_SMMUV3_NODE              *Info;
  UINT8                           SocketIndex;
  UINT8                           SmmuV3NodeIndex;
  UINT32                          EventInt;
  UINT32                          PriInt;
  UINT32                          GerrInt;
  UINT32                          SyncInt;
  UINT8                           Pci0Sub0Bifurcation;
  UINT8                           Pci1Bifurcation;
  UINT8                           Pci2Bifurcation;
  UINT32                          PciBifurcation;

  Info = gPlatformInfo.SmmuV3Info;
  SmmuV3NodeIndex = 0;
  for (SocketIndex = 0; SocketIndex < gSocketCount; SocketIndex++) {
    PciBifurcation = GetPciBifurcation (SocketIndex);
    Pci0Sub0Bifurcation = PciBifurcation & 0xF;
    Pci1Bifurcation = (PciBifurcation >> 8) & 0xF;
    Pci2Bifurcation = (PciBifurcation >> 12) & 0xF;

    if (EnablePcieSmmu) {
      //Smmu0
      if (PCI_BYPASS != Pci0Sub0Bifurcation) {
        Info[SmmuV3NodeIndex].Token = (SocketIndex << 8) | (SMMUV3_NODE_0_TOKEN);
        Info[SmmuV3NodeIndex].IdMappingToken = (SocketIndex << 8) | (SMMUV3_NODE_0_ID_MAPPING_TOKEN);
        Info[SmmuV3NodeIndex].IdMappingCount = gPlatformInfo.Smmu0IdMappingCount[SocketIndex];
        Info[SmmuV3NodeIndex].BaseAddress = SocketIndex * SOCKET_ADDRESS_OFFSEST + PCI0_SMMU_BASE;
        Info[SmmuV3NodeIndex].Flags = EFI_ACPI_IORT_SMMUv3_FLAG_COHAC_OVERRIDE | EFI_ACPI_IORT_SMMUv3_FLAG_PROXIMITY_DOMAIN;
        Info[SmmuV3NodeIndex].VatosAddress = 0x0;
        Info[SmmuV3NodeIndex].Model = 0x0;
        EventInt = SocketIndex * SMMU_INTERRUPT_OFSSET + EVENT_INTERRUPT_BASE + 0 * EVENT_INTERRUPT_OFFSET;
        PriInt = SocketIndex * SMMU_INTERRUPT_OFSSET + PRI_INTERRUPT_BASE + 0 * PRI_INTERRUPT_OFFSET;
        GerrInt = SocketIndex * SMMU_INTERRUPT_OFSSET + GERR_INTERRUPT_BASE + 0 * GERR_INTERRUPT_OFFSET;
        SyncInt = SocketIndex * SMMU_INTERRUPT_OFSSET + SYNC_INTERRUPT_BASE + 0 * SYNC_INTERRUPT_OFFSET;
        Info[SmmuV3NodeIndex].EventInterrupt = EventInt;
        Info[SmmuV3NodeIndex].PriInterrupt = PriInt;
        Info[SmmuV3NodeIndex].GerrInterrupt = GerrInt;
        Info[SmmuV3NodeIndex].SyncInterrupt = SyncInt;
        Info[SmmuV3NodeIndex].ProximityDomain = SocketIndex;
        Info[SmmuV3NodeIndex].DeviceIdMappingIndex = 0x0;
        SmmuV3NodeIndex++;
      }

      //Smmu1
      if ((C2C_C2C != Pci1Bifurcation) && (PCI_BYPASS != Pci1Bifurcation)) {
        Info[SmmuV3NodeIndex].Token = (SocketIndex << 8) | (SMMUV3_NODE_1_TOKEN);
        Info[SmmuV3NodeIndex].IdMappingToken = (SocketIndex << 8) | (SMMUV3_NODE_1_ID_MAPPING_TOKEN);
        Info[SmmuV3NodeIndex].IdMappingCount = 0x1;
        Info[SmmuV3NodeIndex].BaseAddress = SocketIndex * SOCKET_ADDRESS_OFFSEST + PCI1_SMMU_BASE;
        Info[SmmuV3NodeIndex].Flags = EFI_ACPI_IORT_SMMUv3_FLAG_COHAC_OVERRIDE | EFI_ACPI_IORT_SMMUv3_FLAG_PROXIMITY_DOMAIN;
        Info[SmmuV3NodeIndex].VatosAddress = 0x0;
        Info[SmmuV3NodeIndex].Model = 0x0;
        EventInt = SocketIndex * SMMU_INTERRUPT_OFSSET + EVENT_INTERRUPT_BASE + 1 * EVENT_INTERRUPT_OFFSET;
        PriInt = SocketIndex * SMMU_INTERRUPT_OFSSET + PRI_INTERRUPT_BASE + 1 * PRI_INTERRUPT_OFFSET;
        GerrInt = SocketIndex * SMMU_INTERRUPT_OFSSET + GERR_INTERRUPT_BASE + 1 * GERR_INTERRUPT_OFFSET;
        SyncInt = SocketIndex * SMMU_INTERRUPT_OFSSET + SYNC_INTERRUPT_BASE + 1 * SYNC_INTERRUPT_OFFSET;
        Info[SmmuV3NodeIndex].EventInterrupt = EventInt;
        Info[SmmuV3NodeIndex].PriInterrupt = PriInt;
        Info[SmmuV3NodeIndex].GerrInterrupt = GerrInt;
        Info[SmmuV3NodeIndex].SyncInterrupt = SyncInt;
        Info[SmmuV3NodeIndex].ProximityDomain = SocketIndex;
        Info[SmmuV3NodeIndex].DeviceIdMappingIndex = 0x0;
        SmmuV3NodeIndex++;
      }

      //Smmu2
      if ((C2C_C2C != Pci2Bifurcation) && (PCI_BYPASS != Pci2Bifurcation)) {
        Info[SmmuV3NodeIndex].Token = (SocketIndex << 8) | (SMMUV3_NODE_2_TOKEN);
        Info[SmmuV3NodeIndex].IdMappingToken = (SocketIndex << 8) | (SMMUV3_NODE_2_ID_MAPPING_TOKEN);
        Info[SmmuV3NodeIndex].IdMappingCount = 0x1;
        Info[SmmuV3NodeIndex].BaseAddress = SocketIndex * SOCKET_ADDRESS_OFFSEST + PCI2_SMMU_BASE;
        Info[SmmuV3NodeIndex].Flags = EFI_ACPI_IORT_SMMUv3_FLAG_COHAC_OVERRIDE | EFI_ACPI_IORT_SMMUv3_FLAG_PROXIMITY_DOMAIN;
        Info[SmmuV3NodeIndex].VatosAddress = 0x0;
        Info[SmmuV3NodeIndex].Model = 0x0;
        EventInt = SocketIndex * SMMU_INTERRUPT_OFSSET + EVENT_INTERRUPT_BASE + 2 * EVENT_INTERRUPT_OFFSET;
        PriInt = SocketIndex * SMMU_INTERRUPT_OFSSET + PRI_INTERRUPT_BASE + 2 * PRI_INTERRUPT_OFFSET;
        GerrInt = SocketIndex * SMMU_INTERRUPT_OFSSET + GERR_INTERRUPT_BASE + 2 * GERR_INTERRUPT_OFFSET;
        SyncInt = SocketIndex * SMMU_INTERRUPT_OFSSET + SYNC_INTERRUPT_BASE + 2 * SYNC_INTERRUPT_OFFSET;
        Info[SmmuV3NodeIndex].EventInterrupt = EventInt;
        Info[SmmuV3NodeIndex].PriInterrupt = PriInt;
        Info[SmmuV3NodeIndex].GerrInterrupt = GerrInt;
        Info[SmmuV3NodeIndex].SyncInterrupt = SyncInt;
        Info[SmmuV3NodeIndex].ProximityDomain = SocketIndex;
        Info[SmmuV3NodeIndex].DeviceIdMappingIndex = 0x0;
        SmmuV3NodeIndex++;
      }
    }

    if (EnableCeuSmmu) {
      //CEU Smmu
      Info[SmmuV3NodeIndex].Token = (SocketIndex << 8) | (CEU_SMMUV3_NODE_TOKEN);
      Info[SmmuV3NodeIndex].IdMappingToken = 0x0;
      Info[SmmuV3NodeIndex].IdMappingCount = 0x0;
      Info[SmmuV3NodeIndex].BaseAddress = SocketIndex * SOCKET_ADDRESS_OFFSEST + CEU_SMMU_BASE;
      Info[SmmuV3NodeIndex].Flags = EFI_ACPI_IORT_SMMUv3_FLAG_COHAC_OVERRIDE | EFI_ACPI_IORT_SMMUv3_FLAG_PROXIMITY_DOMAIN;
      Info[SmmuV3NodeIndex].VatosAddress = 0x0;
      Info[SmmuV3NodeIndex].Model = 0x0;
      EventInt = SocketIndex * SMMU_INTERRUPT_OFSSET + CEU_EVENT_INTERRUPT_BASE;
      PriInt = SocketIndex * SMMU_INTERRUPT_OFSSET + CEU_PRI_INTERRUPT_BASE;
      GerrInt = SocketIndex * SMMU_INTERRUPT_OFSSET + CEU_GERR_INTERRUPT_BASE;
      SyncInt = SocketIndex * SMMU_INTERRUPT_OFSSET + CEU_SYNC_INTERRUPT_BASE;
      Info[SmmuV3NodeIndex].EventInterrupt = EventInt;
      Info[SmmuV3NodeIndex].PriInterrupt = PriInt;
      Info[SmmuV3NodeIndex].GerrInterrupt = GerrInt;
      Info[SmmuV3NodeIndex].SyncInterrupt = SyncInt;
      Info[SmmuV3NodeIndex].ProximityDomain = SocketIndex;
      Info[SmmuV3NodeIndex].DeviceIdMappingIndex = 0x0;
      SmmuV3NodeIndex++;
    }
  }

  gSmmuV3Count = SmmuV3NodeIndex;
}

VOID
DumpSmmuV3Info (
  VOID
  )
{
  CM_ARM_SMMUV3_NODE     *Info;
  UINT8                  Index;

  Info = gPlatformInfo.SmmuV3Info;
  for (Index = 0; Index < gSmmuV3Count; Index++) {
    DEBUG ((DEBUG_INFO, "SmmuV3Node[%d].Token:0x%x\n", Index, Info[Index].Token));
    DEBUG ((DEBUG_INFO, "SmmuV3Node[%d].IdMappingToken:0x%x\n", Index, Info[Index].IdMappingToken));
    DEBUG ((DEBUG_INFO, "SmmuV3Node[%d].IdMappingCount:%d\n", Index, Info[Index].IdMappingCount));
    DEBUG ((DEBUG_INFO, "\n"));
  }
}

/**
  A helper function for init pci0 IdMappingInfo.

  @param [in]  SocketId                Socket Id.
  @param [in]  Pci0LastRootPortNo      Pci0 last rootport number.
  @param [in]  Pci0Sub0LastRootPortNo  Pci0 pcie5 Subsystem last rootport number.
  @param [in]  EnableSmmu              Smmu is enable.
**/
VOID
InitPci0IdMappingInfoHelper (
  IN UINT8   SocketId,
  IN INT8   Pci0LastRootPortNo,
  IN INT8   Pci0Sub0LastRootPortNo,
  IN BOOLEAN EnableSmmu
  )
{
  UINT32  RootComplexIdMappingIndex;
  UINT32  Smmu0IdMappingIndex;
  UINTN   Token;
  UINT8   SubBusStart;
  UINT8   SubBusEnd;

  RootComplexIdMappingIndex = SocketId * 8 + ROOT_COMPLEX_ID_MAPPING_OFFSET;
  Smmu0IdMappingIndex = SocketId * 8 + SMMUV3_NODE_0_ID_MAPPING_OFFSET;

  if ((-1 == Pci0Sub0LastRootPortNo) && (-1 == Pci0LastRootPortNo)) {
    return;
  }
  if (-1 == Pci0Sub0LastRootPortNo) {
    //RootComplex IdMapping
    //C4/C5/C6/C7 ->Its3
    Token = (SocketId << 8) | ITS_NODE_3_TOKEN;
    SubBusStart = GetSecondaryBusNumber (SocketId, 0);
    SubBusEnd = GetSubordinateBusNumber (SocketId, Pci0LastRootPortNo);
    FillIdMappingInfo (RootComplexIdMappingIndex, SubBusStart, SubBusEnd, Token, 0);
    gPlatformInfo.RootComplexIdMappingCount[SocketId] += 1;
  } else {
    if (EnableSmmu) {
      //RootComplex IdMapping
      //1.C0 ~ C3 -> Smmu0 -> Its2
      SubBusStart = GetSecondaryBusNumber (SocketId, 0);
      SubBusEnd = GetSubordinateBusNumber (SocketId, Pci0Sub0LastRootPortNo);
      Token = (SocketId << 8) | SMMUV3_NODE_0_TOKEN;
      FillIdMappingInfo (RootComplexIdMappingIndex, SubBusStart, SubBusEnd, Token, 0);
      gPlatformInfo.RootComplexIdMappingCount[SocketId] += 1;
      //2.C4 ~ C7 -> Its3
      if (Pci0LastRootPortNo != Pci0Sub0LastRootPortNo) {
        SubBusStart = GetSecondaryBusNumber (SocketId, Pci0Sub0LastRootPortNo + 1);
        SubBusEnd = GetSubordinateBusNumber (SocketId, Pci0LastRootPortNo);
        Token = (SocketId << 8) | ITS_NODE_3_TOKEN;
             FillIdMappingInfo (RootComplexIdMappingIndex + 1, SubBusStart, SubBusEnd, Token, 0);
             gPlatformInfo.RootComplexIdMappingCount[SocketId] += 1;
      }

      //Smmu IdMapping
      //C0 -> Smmu0 -> Its2
      SubBusStart = GetSecondaryBusNumber (SocketId, 0);
      SubBusEnd = GetSubordinateBusNumber (SocketId, 0);
      Token = (SocketId << 8) | ITS_NODE_2_TOKEN;
      FillIdMappingInfo (Smmu0IdMappingIndex, SubBusStart, SubBusEnd, Token, 0);
      gPlatformInfo.Smmu0IdMappingCount[SocketId] += 1;
      //C2 ~ C3 -> Smmu0 -> Its3
      if (Pci0Sub0LastRootPortNo > 0) {
        SubBusStart = GetSecondaryBusNumber (SocketId, 1);
        SubBusEnd = GetSubordinateBusNumber (SocketId, Pci0Sub0LastRootPortNo);
        Token = (SocketId << 8) | ITS_NODE_3_TOKEN;
        FillIdMappingInfo (Smmu0IdMappingIndex + 1, SubBusStart, SubBusEnd, Token, 0);
        gPlatformInfo.Smmu0IdMappingCount[SocketId] += 1;
      }
    } else {
      //RootComplex IdMapping
      //1.C0 -> Its2
      SubBusStart = GetSecondaryBusNumber (SocketId, 0);
      SubBusEnd = GetSubordinateBusNumber (SocketId, 0);
      Token = (SocketId << 8) | ITS_NODE_2_TOKEN;
      FillIdMappingInfo (RootComplexIdMappingIndex, SubBusStart, SubBusEnd, Token, 0);
      gPlatformInfo.RootComplexIdMappingCount[SocketId] += 1;
      if (Pci0LastRootPortNo > 0) {
        //2.C1 ~ C7 -> Its3
        SubBusStart = GetSecondaryBusNumber (SocketId, 1);
        SubBusEnd = GetSubordinateBusNumber (SocketId, Pci0LastRootPortNo);
        Token = (SocketId << 8) | ITS_NODE_3_TOKEN;
        FillIdMappingInfo (RootComplexIdMappingIndex + 1, SubBusStart, SubBusEnd, Token, 0);
        gPlatformInfo.RootComplexIdMappingCount[SocketId] += 1;
      }
    }
  }
}

/**
  Init Pci0 IdMappingInfo.

  @param [in]  SocketId                Socket Id.
  @param [in]  Pci0Sub0Bifurcation     Pci0 pcie5 subsystem bifurcation.
  @param [in]  Pci0Sub1Bifurcation     Pci0 pcie3 Subsystem bifurcation.
  @param [in]  EnableSmmu              Smmu is enable.
**/
UINT8
InitPci0IdMappingInfo (
  IN UINT8     SocketId,
  IN UINT8     Pci0Sub0Bifurcation,
  IN UINT8     Pci0Sub1Bifurcation,
  IN BOOLEAN   EnableSmmu
)
{
  INT8 Pci0Sub0LastRootPortNo;
  INT8 Pci0LastRootPortNo;

  switch (Pci0Sub0Bifurcation) {
    case X16:
      Pci0Sub0LastRootPortNo = 0;
      if (PCI_BYPASS != Pci0Sub1Bifurcation) {
        Pci0LastRootPortNo = 4;
      } else {
        Pci0LastRootPortNo = 0;
      }
      break;
    case X8X8:
      Pci0Sub0LastRootPortNo = 1;
      if (PCI_BYPASS != Pci0Sub1Bifurcation) {
        Pci0LastRootPortNo = 5;
      } else {
        Pci0LastRootPortNo = 1;
      }
      break;
    case X8X4X4:
      Pci0Sub0LastRootPortNo = 2;
      if (PCI_BYPASS != Pci0Sub1Bifurcation) {
        Pci0LastRootPortNo = 6;
      } else {
        Pci0LastRootPortNo = 2;
      }
      break;
    case X4X4X4X4:
      Pci0Sub0LastRootPortNo = 3;
      if (PCI_BYPASS != Pci0Sub1Bifurcation) {
        Pci0LastRootPortNo = 7;
      } else {
        Pci0LastRootPortNo = 3;
      }
      break;
    case PCI_BYPASS:
      Pci0Sub0LastRootPortNo = -1;
      if (PCI_BYPASS != Pci0Sub1Bifurcation) {
        Pci0LastRootPortNo = 3;
      } else {
        Pci0LastRootPortNo = -1;
      }
      break;

    default: {
      DEBUG ((DEBUG_ERROR, "Unkonwn Pci Bifurcation \n"));
      break;
    }
  }

  InitPci0IdMappingInfoHelper (SocketId, Pci0LastRootPortNo, Pci0Sub0LastRootPortNo, EnableSmmu);

  return Pci0LastRootPortNo;
}

/**
  A helper function for init pci1 IdMappingInfo.

  @param [in]  SocketId         Socket Id.
  @param [in]  SubBusStart      Pci1 system first bus number.
  @param [in]  SubBusEnd        Pci1 system last bus number.
  @param [in]  EnableSmmu       Smmu is enable.
**/
VOID
InitPci1IdMappingInfoHelper (
  IN UINT8   SocketId,
  IN UINT8   SubBusStart,
  IN UINT8   SubBusEnd,
  IN BOOLEAN EnableSmmu
  )
{
  UINTN Token;
  UINT8 RootComplexIdMappingIndex;
  UINT8 Smmu1IdMappingIndex;
  UINT8 IdMappingOffset;

  IdMappingOffset = gPlatformInfo.RootComplexIdMappingCount[SocketId];
  RootComplexIdMappingIndex = SocketId * 8 + ROOT_COMPLEX_ID_MAPPING_OFFSET + IdMappingOffset;
  Smmu1IdMappingIndex = SocketId * 8 + SMMUV3_NODE_1_ID_MAPPING_OFFSET;
  if (EnableSmmu) {
    //C8/C9 -> Smmu1 -> ITS0
    Token = (SocketId << 8) | SMMUV3_NODE_1_TOKEN;
  } else {
    //C8/C9 -> ITS0
    Token = (SocketId << 8) | ITS_NODE_0_TOKEN;
  }
  FillIdMappingInfo (RootComplexIdMappingIndex, SubBusStart, SubBusEnd, Token, 0);
  gPlatformInfo.RootComplexIdMappingCount[SocketId] += 1;

  //Smmu IdMapping
  Token = (SocketId << 8) | ITS_NODE_0_TOKEN;
  FillIdMappingInfo (Smmu1IdMappingIndex, SubBusStart, SubBusEnd, Token, 0);
}

/**
  Init Pci1 IdMappingInfo.

  @param [in]  SocketId                Socket Id.
  @param [in]  PciBifurcation          Pci bifurcation.
  @param [in]  Pci0LastRootPortNo      Pci0 system last rootport number.
  @param [in]  EnableSmmu              Smmu is enable.
**/
UINT8
InitPci1IdMappingInfo (
  IN UINT8   SocketId,
  IN UINT8   PciBifurcation,
  IN INT8   Pci0LastRootPortNo,
  IN BOOLEAN EnableSmmu
)
{
  UINT8 SubBusStart;
  UINT8 SubBusEnd;
  INT8 Pci1LastRootPortNo;

  switch (PciBifurcation) {
    case X8X8:
      SubBusStart = GetSecondaryBusNumber (SocketId, Pci0LastRootPortNo + 1);
      SubBusEnd = GetSubordinateBusNumber (SocketId, Pci0LastRootPortNo + 2);
      Pci1LastRootPortNo = Pci0LastRootPortNo + 2;
      break;

    case X8_C2C:
      SubBusStart = GetSecondaryBusNumber (SocketId, Pci0LastRootPortNo + 1);
      SubBusEnd = GetSubordinateBusNumber (SocketId, Pci0LastRootPortNo + 1);
      Pci1LastRootPortNo = Pci0LastRootPortNo + 1;
      break;

    case C2C_X8:
      SubBusStart = GetSecondaryBusNumber (SocketId, Pci0LastRootPortNo + 1);
      SubBusEnd = GetSubordinateBusNumber (SocketId, Pci0LastRootPortNo + 1);
      Pci1LastRootPortNo = Pci0LastRootPortNo + 1;
      break;

    case C2C_C2C:
      Pci1LastRootPortNo = Pci0LastRootPortNo;
      break;

    case PCI_BYPASS:
      Pci1LastRootPortNo = Pci0LastRootPortNo;
      break;

    default: {
      DEBUG ((DEBUG_ERROR, "Unkonwn Pci Bifurcation \n"));
      break;
    }
  }

  if (Pci1LastRootPortNo > Pci0LastRootPortNo) {
    InitPci1IdMappingInfoHelper (SocketId, SubBusStart, SubBusEnd, EnableSmmu);
  }

  return Pci1LastRootPortNo;
}

/**
  A helper function for init pci2 IdMappingInfo.

  @param [in]  SocketId         Socket Id.
  @param [in]  SubBusStart      Pci2 system first bus number.
  @param [in]  SubBusEnd        Pci2 system last bus number.
  @param [in]  EnableSmmu       Smmu is enable.
**/
VOID
InitPci2IdMappingInfoHelper (
  IN UINT8   SocketId,
  IN UINT8   SubBusStart,
  IN UINT8   SubBusEnd,
  IN BOOLEAN EnableSmmu
  )
{
  UINTN Token;
  UINT8 RootComplexIdMappingIndex;
  UINT8 Smmu2IdMappingIndex;
  UINT8 Offset;

  Offset = gPlatformInfo.RootComplexIdMappingCount[SocketId];
  RootComplexIdMappingIndex = SocketId * 8 + ROOT_COMPLEX_ID_MAPPING_OFFSET + Offset;
  Smmu2IdMappingIndex = SocketId * 8 + SMMUV3_NODE_2_ID_MAPPING_OFFSET;
  if (EnableSmmu) {
    //C10/C11 -> Smmu2 -> ITS1
    Token = (SocketId << 8) | SMMUV3_NODE_2_TOKEN;
  } else {
    //C10/C11 -> ITS1
    Token = (SocketId << 8) | ITS_NODE_1_TOKEN;
  }
  FillIdMappingInfo (RootComplexIdMappingIndex, SubBusStart, SubBusEnd, Token, 0);
  gPlatformInfo.RootComplexIdMappingCount[SocketId] += 1;

  //Smmu IdMapping
  Token = (SocketId << 8) | ITS_NODE_1_TOKEN;
  FillIdMappingInfo (Smmu2IdMappingIndex, SubBusStart, SubBusEnd, Token, 0);
}

/**
  Init Pci2 IdMappingInfo.

  @param [in]  SocketId                Socket Id.
  @param [in]  PciBifurcation          Pci bifurcation.
  @param [in]  Pci1LastRootPortNo      Pci1 system last rootport number.
  @param [in]  EnableSmmu              Smmu is enable.
**/
VOID
InitPci2IdMappingInfo (
  IN UINT8   SocketId,
  IN UINT8   PciBifurcation,
  IN INT8   Pci1LastRootPortNo,
  IN BOOLEAN EnableSmmu
)
{
  UINT8 SubBusStart;
  UINT8 SubBusEnd;
  INT8 Pci2LastRootPortNo;

  switch (PciBifurcation) {
    case X8X8:
      SubBusStart = GetSecondaryBusNumber (SocketId, Pci1LastRootPortNo + 1);
      SubBusEnd = GetSubordinateBusNumber (SocketId, Pci1LastRootPortNo + 2);
      Pci2LastRootPortNo = Pci1LastRootPortNo + 2;
      break;

    case X8_C2C:
      SubBusStart = GetSecondaryBusNumber (SocketId, Pci1LastRootPortNo + 1);
      SubBusEnd = GetSubordinateBusNumber (SocketId, Pci1LastRootPortNo + 1);
      Pci2LastRootPortNo = Pci1LastRootPortNo + 1;
      break;

    case C2C_X8:
      SubBusStart = GetSecondaryBusNumber (SocketId, Pci1LastRootPortNo + 1);
      SubBusEnd = GetSubordinateBusNumber (SocketId, Pci1LastRootPortNo + 1);
      Pci2LastRootPortNo = Pci1LastRootPortNo + 1;
      break;

    case C2C_C2C:
      Pci2LastRootPortNo = Pci1LastRootPortNo;
      break;

    case PCI_BYPASS:
      Pci2LastRootPortNo = Pci1LastRootPortNo;
      break;

    default: {
      DEBUG ((DEBUG_ERROR, "Unkonwn Pci Bifurcation \n"));
      break;
    }
  }

  if (Pci2LastRootPortNo > Pci1LastRootPortNo) {
    InitPci2IdMappingInfoHelper (SocketId, SubBusStart, SubBusEnd, EnableSmmu);
  }
}

/**
  Init single socket IdMappingInfo.

  @param [in]  SocketId                Socket Id.
  @param [in]  EnableSmmu              Smmu is enable.
**/
VOID
InitIdMappingSingleSocket (
  IN UINT8 SocketId,
  IN BOOLEAN  EnableSmmu
  )
{
  UINT32  PciBifurcation;
  UINT8   Pci0Sub0Bifurcation;
  UINT8   Pci0Sub1Bifurcation;
  UINT8   Pci1Bifurcation;
  UINT8   Pci2Bifurcation;
  INT8   Pci0LastRootPortNo;
  INT8   Pci1LastRootPortNo;

  PciBifurcation = GetPciBifurcation (SocketId);
  Pci0Sub0Bifurcation = PciBifurcation & 0xF;
  Pci0Sub1Bifurcation = (PciBifurcation >> 4) & 0xF;
  Pci1Bifurcation = (PciBifurcation >> 8) & 0xF;
  Pci2Bifurcation = (PciBifurcation >> 12) & 0xF;

  Pci0LastRootPortNo = InitPci0IdMappingInfo (SocketId, Pci0Sub0Bifurcation, Pci0Sub1Bifurcation, EnableSmmu);
  Pci1LastRootPortNo = InitPci1IdMappingInfo (SocketId, Pci1Bifurcation, Pci0LastRootPortNo, EnableSmmu);
  InitPci2IdMappingInfo (SocketId, Pci2Bifurcation, Pci1LastRootPortNo, EnableSmmu);
}

/**
  Init All IdMappingInfo.

  @param [in]  EnablePcieSmmu   Control Pcie Smmu Enable/Disable.
**/
VOID
InitIdMappingArrayInfo (
  IN BOOLEAN EnablePcieSmmu
  )
{
  UINT8                             SocketIndex;

  for (SocketIndex = 0; SocketIndex < gSocketCount; SocketIndex++) {
    InitIdMappingSingleSocket (SocketIndex, EnablePcieSmmu);
  }
}


/**
  Return GitItsIdentifierArray Info.

  @param [in]      Token               A unique token for identifying the requested
                                       CM_ARM_ITS_IDENTIFIER object.
  @param [in, out] ItsIdentifier       A pointer to a pointer to the CM_ARM_ITS_IDENTIFIER object.
  @param [in, out] ItsIdentifierCount  The Count of object.

  @retval EFI_SUCCESS                  Success.
**/
EFI_STATUS
EFIAPI
GetEArmObjGicItsIdentifierArray (
  IN     CM_OBJECT_TOKEN        Token,
  IN OUT CM_ARM_ITS_IDENTIFIER  **ItsIdentifier,
  IN OUT UINT32                 *ItsIdentifierCount
  )
{
  *ItsIdentifierCount = 1;
  *ItsIdentifier = &gPlatformInfo.ItsIdentifierInfo[Token];

  return EFI_SUCCESS;
}

/**
  Return GitItsGroup Info.

  @param [in, out] ItsGroupNodeList    A pointer to a pointer to the CM_ARM_ITS_GROUP_NODE object.
  @param [in, out] ItsGroupNodeCount   The Count of object.

  @retval EFI_SUCCESS                  Success.
**/
EFI_STATUS
EFIAPI
GetEArmObjItsGroup (
  IN OUT  CM_ARM_ITS_GROUP_NODE  **ItsGroupNodeList,
  IN OUT  UINT32                 *ItsGroupNodeCount
  )
{

  *ItsGroupNodeCount = gItsGroupCount;
  *ItsGroupNodeList = (CM_ARM_ITS_GROUP_NODE *)&gPlatformInfo.ItsGroupInfo;

  return EFI_SUCCESS;
}

/**
  Return NamedComponent Info.

  @param [in, out] NamedComponentNodeList   A pointer to a pointer to the CM_ARM_NAMED_COMPONENT_NODE object.
  @param [in, out] NamedComponentNodeCount  The Count of object.

  @retval EFI_SUCCESS                  Success.
  @retval EFI_NOT_FOUND                The required object information is not found.
**/
EFI_STATUS
EFIAPI
GetEArmObjNamedComponent (
  IN OUT  CM_ARM_NAMED_COMPONENT_NODE  **NamedComponentNodeList,
  IN OUT  UINT32                       *NamedComponentNodeCount
  )
{
  *NamedComponentNodeList = (CM_ARM_NAMED_COMPONENT_NODE *)&gPlatformInfo.CeuNode;
  *NamedComponentNodeCount = gNamedComponentCount;

  return EFI_SUCCESS;
}

/**
  Return RootComplex Info.

  @param [in, out] RootComplexNodeList   A pointer to a pointer to the CM_ARM_ROOT_COMPLEX_NODE object.
  @param [in, out] RootComplexNodeCount  The Count of object.

  @retval EFI_SUCCESS                    Success.
**/
EFI_STATUS
EFIAPI
GetEArmObjRootComplex (
  IN OUT CM_ARM_ROOT_COMPLEX_NODE  **RootComplexNodeList,
  IN OUT UINT32                    *RootComplexNodeCount
  )
{
  *RootComplexNodeCount = gRootComplexCount;
  *RootComplexNodeList = (CM_ARM_ROOT_COMPLEX_NODE *)&gPlatformInfo.RootComplexInfo;

  return EFI_SUCCESS;
}

/**
  Return SmmuV1/SmmuV2 Info.

  @param [in, out] SmmuV1V2NodeList      A pointer to a pointer to the CM_ARM_SMMUV1_SMMUV2_NODE object.
  @param [in, out] SmmuV1V2NodeCount     The Count of object.

  @retval EFI_SUCCESS                    Success.
  @retval EFI_NOT_FOUND                  The required object information is not found.
**/
EFI_STATUS
EFIAPI
GetEArmObjSmmuV1SmmuV2 (
  IN OUT CM_ARM_SMMUV1_SMMUV2_NODE **SmmuV1V2NodeList,
  IN OUT UINT32                    *SmmuV1V2NodeCount
  )
{
  return EFI_NOT_FOUND;
}

/**
  Return Smmu Interrupt Info.

  @param [in]      InterruptToken        A unique token for identifying the requested
                                         CM_ARM_SMMU_INTERRUPT object.
  @param [in, out] SmmuInterrupt         A pointer to a pointer to the CM_ARM_SMMU_INTERRUPT object.
  @param [in, out] SmmuInterruptCount    The Count of object.

  @retval EFI_SUCCESS                    Success.
  @retval EFI_NOT_FOUND                  The required object information is not found.
**/
EFI_STATUS
EFIAPI
GetEArmObjSmmuInterruptArray (
  IN     CM_OBJECT_TOKEN        InterruptToken,
  IN OUT CM_ARM_SMMU_INTERRUPT  **SmmuInterrupt,
  IN OUT UINT32                 *SmmuInterruptCount
  )
{
  return EFI_NOT_FOUND;
}

/**
  Return SmmuV3 Info.

  @param [in, out] SmmuV3NodeList        A pointer to a pointer to the CM_ARM_SMMUV3_NODE object.
  @param [in, out] SmmuV3NodeCount       The Count of object.

  @retval EFI_SUCCESS                    Success.
  @retval EFI_NOT_FOUND                  The required object information is not found.
**/
EFI_STATUS
EFIAPI
GetEArmObjSmmuV3 (
  IN OUT CM_ARM_SMMUV3_NODE  **SmmuV3NodeList,
  IN OUT UINT32              *SmmuV3NodeCount
  )
{
  *SmmuV3NodeList = (CM_ARM_SMMUV3_NODE *)&gPlatformInfo.SmmuV3Info[0];
  /**SmmuV3NodeCount = gSocketCount * SMMU_NODE_COUNT;*/
  *SmmuV3NodeCount = gSmmuV3Count;

  return EFI_SUCCESS;
}

/**
  Return Pmgc Info.

  @param [in, out] PmcgNodeList   A pointer to a pointer to the CM_ARM_PMCG_NODE object.
  @param [in, out] PmcgNodeCount  The Count of object.

  @retval EFI_SUCCESS                    Success.
  @retval EFI_NOT_FOUND                  The required object information is not found.
**/
EFI_STATUS
EFIAPI
GetEArmObjPmcg (
  IN OUT CM_ARM_PMCG_NODE    **PmcgNodeList,
  IN OUT UINT32              *PmcgNodeCount
  )
{
  return EFI_NOT_FOUND;
}

/**
  Return IdMapping Info.

  @param [in]      IdMappingToken        A unique token for identifying the requested
                                         CM_ARM_ID_MAPPING object.
  @param [in, out] IdMappings            A pointer to a pointer to the CM_ARM_ID_MAPPING object.
  @param [in, out] IdMappingCount        The Count of object.

  @retval EFI_SUCCESS                    Success.
  @retval EFI_NOT_FOUND                  The required object information is not found.
**/
EFI_STATUS
EFIAPI
GetEArmObjIdMappingArray (
  IN     CM_OBJECT_TOKEN    IdMappingToken,
  IN OUT CM_ARM_ID_MAPPING  **IdMappings,
  IN OUT UINT32             *IdMappingCount
  )
{
  UINT8 Type;
  UINT8 SocketId;
  UINT8 Index;

  Type = GET_TOKEN_TYPE(IdMappingToken);
  SocketId = GET_TOKEN_SOCKETID (IdMappingToken);

  switch (Type) {
    case ROOT_COMPLEX_ID_MAPPING_TOKEN:
      Index = SocketId * 8 + ROOT_COMPLEX_ID_MAPPING_OFFSET;
      *IdMappingCount = gPlatformInfo.RootComplexIdMappingCount[SocketId];
      *IdMappings = (CM_ARM_ID_MAPPING *)&gPlatformInfo.IdMappingInfo[Index];
      break;

    case SMMUV3_NODE_0_ID_MAPPING_TOKEN:
      Index = SocketId * 8 + SMMUV3_NODE_0_ID_MAPPING_OFFSET;
      *IdMappingCount = gPlatformInfo.Smmu0IdMappingCount[SocketId];
      *IdMappings = (CM_ARM_ID_MAPPING *)&gPlatformInfo.IdMappingInfo[Index];
      break;

    case SMMUV3_NODE_1_ID_MAPPING_TOKEN:
      Index = SocketId * 8 + SMMUV3_NODE_1_ID_MAPPING_OFFSET;
      *IdMappingCount = 0x1;
      *IdMappings = (CM_ARM_ID_MAPPING *)&gPlatformInfo.IdMappingInfo[Index];
      break;

    case SMMUV3_NODE_2_ID_MAPPING_TOKEN:
      Index = SocketId * 8 + SMMUV3_NODE_2_ID_MAPPING_OFFSET;
      *IdMappingCount = 0x1;
      *IdMappings = (CM_ARM_ID_MAPPING *)&gPlatformInfo.IdMappingInfo[Index];
      break;

    case CEU_ID_MAPPING_TOKEN:
      Index = SocketId;
      *IdMappingCount = 0x1;
      *IdMappings = (CM_ARM_ID_MAPPING *)&gPlatformInfo.CeuIdMappings[Index];
      break;

    default: {
      DEBUG ((
        DEBUG_ERROR,
        "Not Find IDMapping Info, Token:%d\n",
        Type));
      return EFI_NOT_FOUND;
    }
  }

  return EFI_SUCCESS;
}

/**
  Initialize the platform info.
**/
VOID
InitPlatformInfo (
  VOID
  )
{
  BOOLEAN    EnablePcieSmmu;
  BOOLEAN    EnableCeuSmmu;

  EnablePcieSmmu = TRUE;
  EnableCeuSmmu  = TRUE;

  gSmmuV3Count = 0;
  gNamedComponentCount = 0;
  InitSocketNumber();
  InitIdMappingArrayInfo (EnablePcieSmmu);
  InitGicItsIdentifierArrayInfo ();
  InitItsGroupInfo ();
  InitSmmuV3Info (EnablePcieSmmu, EnableCeuSmmu);
  InitRootComplexInfo ();
  InitNamedComponentInfo (EnableCeuSmmu);
  DumpItsGroupInfo ();
  DumpRootComplexInfo ();
  DumpSmmuV3Info ();
  DumpIdMappingInfo ();
  DumpNamedComponentInfo ();
}

